/*
 * Copyright 2011 VS Technologies GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

/*	TODO
 *  - display a link after a job has been started to allow the user to track the status of the job 
 *  - add button to refresh list of entity kinds (useful after renaming or deleting entity kinds)
 */
/*global $:false */
(function (window) {

	var ec = {};
	
	const mapReduceBaseUrl = '/mapreduce';	// get this from config
	
	function MapReduceConfig(name) {
		this.configuration = $.parseXML('<configuration name="' + name + '"/>');
		
		this.addProperty = function (name, value) {
			var propElem = this.configuration.createElement("property");
			var nameElem = this.configuration.createElement("name");
			var valueElem = this.configuration.createElement("value");
			
			nameElem.appendChild(this.configuration.createTextNode(name));
			valueElem.appendChild(this.configuration.createTextNode(value));
			
			propElem.appendChild(nameElem);
			propElem.appendChild(valueElem);
			
			this.configuration.documentElement.appendChild(propElem);
		};
		
		this.addMapper = function (mapper) {
			this.addProperty('mapreduce.map.class', mapper);
		};
		
		this.addKind = function (kind) {
			this.addProperty('mapreduce.mapper.inputformat.datastoreinputformat.entitykind', kind);
		};
		
		this.toString = function () {
			try {
				// Gecko- and Webkit-based browsers (Firefox, Chrome), Opera.
				return (new XMLSerializer()).serializeToString(this.configuration);
			} catch (e) {
				try {
					// Internet Explorer.
					return this.configuration.xml;
				} catch (e2) {
					// Other browsers without XML Serializer
					alert('Sorry your browser is not supported. Please consider using Google Chrome.');
				}
			}
			return false;
		};
	
		this.cleanUp = function () {
			this.configuration = null;
		};
		
		// we are using the same InputFormat for all calls
		this.addProperty('mapreduce.inputformat.class', 'com.google.appengine.tools.mapreduce.DatastoreInputFormat');
	}
	
	function block(text) {
		$('#content').fadeTo('fast', 0.5); 
		var info = $('#info');
		info.text(text);
		info.fadeIn();
		$('#content * button').attr('disabled', 'disabled');
	}
	
	function unblock(text, jobId) {
		var info = $('#info');
		
		if (text === undefined) {
			info.empty();
		} else {
			info.text(text);
		}
		if (jobId !== undefined) {
			$('<a>').attr('href', mapReduceBaseUrl + '/detail?mapreduce_id=' + jobId)
					.text('Show MapReduce job details')
					.appendTo(info);
		}
		if (text === undefined && jobId === undefined) {
			info.fadeOut();
		}
		
		$('#content').fadeTo('fast', 1); 
		$('#content * button').removeAttr('disabled');
	}
	
	var advancedOptionsShowing = false;
	function toggleAdvancedOptions(event) {
		var button = $(event.target);
		var table = button.parents('table');
		
		if (advancedOptionsShowing) {
			$(".advancedParameter", table).css('display', 'none');
			button.text('+');
			advancedOptionsShowing = false;
		} else {
			$(".advancedParameter", table).css('display', '');
			button.text('-');
			advancedOptionsShowing = true;
		}
		
		event.stopPropagation();
        return false;
	}
	
	function loadEntityKinds() {
		block('Loading ...');
		$.ajax({
			type : 'GET',
			url : 'command/get_entity_kinds',
			dataType : 'json',
			error : function (request, textStatus, errorThrown) {
				unblock();
				alert("Error:" + textStatus);
			},
			success : function (data, textStatus, request) {
				unblock();
				// append the kinds to all selectors for entity kinds
				$.each($('select[name="kind"]'), function (index, selector) {
					$(selector).empty();
					$.each(data.kinds, function (index, kind) {
						$('<option>').attr('name', kind).text(kind).appendTo(selector);
					});
				});
			}
		});
	}
	
	function startMapReduceJob(conf) {
		block('Running ...');
		$.ajax({
			type : 'POST',
			url : '/mapreduce/start',
			data: {
				configuration: conf.toString()
			},
			dataType : 'json',
			error : function (request, textStatus, errorThrown) {
				unblock(textStatus);
			},
			success : function (data, textStatus, request) {
				unblock('MapReduceJob has been started successfully', data.mapreduce_id);
			}
		});
	}
	
	function executeAddProperty(phase) {
		/* clear any old validation errors */
		var propertyLabel = $('#addProperty * label[for="propertyName"]');
		propertyLabel.removeClass('error');
		var initialValueLabel = $('#addProperty * label[for="initialValue"]');
		initialValueLabel.removeClass('error');
		
		/* validate the property name data */
		var propertyName = $('#addProperty * [name="propertyName"]').val();
		if ($.trim(propertyName).length === 0) {
			propertyLabel.addClass('error');
			alert('property is missing');
			return;
		}
		
		/* TODO more validation on initialValue and better error handling */
		var propertyType = $('#addProperty * [name="propertyType"]').val();
		var initialValue = $('#addProperty * [name="initialValue"]').val();
		if ('Date and time' === propertyType) {
			// TODO better error message
			initialValue = Date.parse(initialValue);
			if (isNaN(initialValue)) {
				initialValueLabel.addClass('error');
				alert('wrong format for the initialValue');
				return;
			}
		}
		
		var kind = $('#addProperty * [name="kind"]').val();
		var conf = new MapReduceConfig('AddPropertyMapper');
		conf.addProperty('job.name', (phase === 'verify' ? "Verify add" : "Add") + " property " + propertyName + " to " + kind);
		conf.addMapper('com.ec.AddPropertyMapper');
		conf.addKind(kind);
		conf.addProperty('ec.propertyName', propertyName);
		conf.addProperty('ec.propertyType', propertyType);
		conf.addProperty('ec.initialValue', initialValue);
		conf.addProperty('ec.phase', phase);
		if (advancedOptionsShowing) {
			conf.addProperty('ec.unindexed', $('#addProperty * [name="unindexed"]').attr('checked'));
		}
		
		startMapReduceJob(conf);
	}
	
	ec.runAddProperty = function () {
		executeAddProperty('run');
	};
	
	ec.verifyAddProperty = function () {
		executeAddProperty('verify');
	};

	function executeDeleteEntityKind(phase) {
		var kind = $('#deleteEntityKind * [name="kind"]').val();
		var conf = new MapReduceConfig('DeleteEntityKindMapper');
		conf.addProperty('job.name', (phase === 'verify' ? "Verify delete" : "Delete") + " entity kind " + kind);
		conf.addMapper('com.ec.DeleteEntityKindMapper');
		conf.addKind(kind);

		startMapReduceJob(conf);
	}

	ec.runDeleteEntityKind = function () {
		executeDeleteEntityKind('run');
	};
	
	ec.verifyDeleteEntityKind = function () {
		executeDeleteEntityKind('verify');
	};
		
	function executeRenameProperty(phase) {
		/* clear any old validation errors */
		var oldPropertyNameLabel = $('#renameProperty * label[for="oldPropertyName"]');
		oldPropertyNameLabel.removeClass('error');
		var newPropertyNameLabel = $('#renameProperty * label[for="newPropertyNameLabel"]');
		newPropertyNameLabel.removeClass('error');
		
		/* validate the property name data */
		var oldPropertyName = $('#renameProperty * [name="oldPropertyName"]').val();
		if ($.trim(oldPropertyName).length === 0) {
			oldPropertyNameLabel.addClass('error');
			alert('old property is missing');
			return;
		}
		var newPropertyName = $('#renameProperty * [name="newPropertyName"]').val();
		if ($.trim(newPropertyName).length === 0) {
			newPropertyNameLabel.addClass('error');
			alert('new property is missing');
			return;
		}
		
		/* create the configuration */
		var kind = $('#renameProperty * [name="kind"]').val();
		var conf = new MapReduceConfig('RenamePropertyMapper');
		conf.addProperty('job.name', (phase === 'verify' ? "Verify rename " : "Rename ") + oldPropertyName + " to " + newPropertyName + " of kind " + kind);
		conf.addMapper('com.ec.RenamePropertyMapper');
		conf.addKind(kind);
		conf.addProperty('ec.oldPropertyName', oldPropertyName);
		conf.addProperty('ec.newPropertyName', newPropertyName);
		conf.addProperty('ec.phase', phase);
		if (advancedOptionsShowing) {
			conf.addProperty('ec.useTransactions', "checked" === $('#renameProperty * [name="useTransactions"]').attr('checked'));
		}

		startMapReduceJob(conf);
	}
	
	ec.runRenameProperty = function () {
		executeRenameProperty('run');
	};
	
	ec.verifyRenameProperty = function () {
		executeRenameProperty('verify');
	};

	function executeRenameKind(phase) {
		/* clear any old validation errors */
		var newKindLabel = $('#renameKind * label[for="newKind"]');
		newKindLabel.removeClass('error');
		
		/* validate the property newKind */
		var newKind = $('#renameKind * [name="newKind"]').val();
		if ($.trim(newKind).length === 0) {
			newKindLabel.addClass('error');
			alert('new kind is missing');
			return;
		}
		
		/* create the configuration */
		var oldKind = $('#renameKind * [name="kind"]').val();
		var conf = new MapReduceConfig('RenameEntityKindMapper');
		conf.addProperty('job.name', (phase === 'verify' ? "Verify rename kind " : "rename kind ") + oldKind + " to " + newKind);
		conf.addMapper('com.ec.RenameEntityKindMapper');
		conf.addKind(oldKind);
		conf.addProperty('ec.newKind', newKind);
		conf.addProperty('ec.phase', phase);
		if (advancedOptionsShowing) {
			conf.addProperty('ec.useTransactions', "checked" === $('#renameKind * [name="useTransactions"]').attr('checked'));
		}

		startMapReduceJob(conf);
	}
	
	ec.runRenameKind = function () {
		executeRenameKind('run');
	};
	
	ec.verifyRenameKind = function () {
		executeRenameKind('verify');
	};
	
	ec.switchTo = function (formId) {
		var navButtons = $('.navButton');
		navButtons.attr('disabled', 'disabled');
		$('.form:visible').fadeOut('fast', function () {
			$(formId).fadeIn('fast', function () {
				navButtons.removeAttr('disabled');
			});
		});
	};
	
	ec.init = function () {
		$(".advancedButton").click(function (event) {
			toggleAdvancedOptions(event);
		});
		loadEntityKinds();
	};
	
	ec.setLogLevel = function () {
		var logLevel = $("#logLevel").val();
		$.ajax({
			type : 'POST',
			url : 'command/set_log_level',
			dataType : 'json',
			data: { level : logLevel }
		});
	};

	window.ec = ec;

})(window);